/*
 *  OpenVPN -- An application to securely tunnel IP networks
 *             over a single TCP/UDP port, with support for SSL/TLS-based
 *             session authentication and key exchange,
 *             packet encryption, packet authentication, and
 *             packet compression.
 *
 *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "openvpn_socket.h"
#include <pthread.h>
#include "error.h"
#include <string.h>

int sockfd_s = -1;
static char ifconfig_local[BUFFER_SIZE];
static char ifconfig_netmask[BUFFER_SIZE];
static int family;
static int prefixLength;
static int tun_mtu;
static char dnsAddresses[BUFFER_SIZE];
static int openvpn_state = OPENVPN_STATE_UNKNOWN;
static int cfg_err_code;
const static char stop_signal[] = "openvpn_close";

extern void throw_signal(const int signum);

//public
int setupClientSocket() {
    msg(M_DEBUG, "opensocket.c setupClientSocket");
    struct sockaddr_un serv_addr;

    sockfd_s = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd_s < 0) {
        msg(M_ERR, "opensocket.c :: ERROR opening socket");
        return -1;
    }
    memset((char *) &serv_addr, 0, sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strcpy(serv_addr.sun_path, SOCK_PATH);

    if (connect(sockfd_s, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        msg(M_ERR, "opensocket.c :: ERROR connecting socket");
        return -1;
    }

    pthread_t t;
    int code = pthread_create(&t, NULL, readMsg, NULL);
    if (code != 0){
        msg(M_ERR, "opensocket.c :: ERROR pthread_create");
    }
    return 0;
}



void setupIfconfig(char* ifconfig,char* netmask, int mtu) {
    msg(M_DEBUG, "opensocket.c :: setupIfconfig");
    strcpy(ifconfig_local, ifconfig);
    strcpy(ifconfig_netmask, netmask);
    tun_mtu = mtu;
    char buffer[BUFFER_SIZE];
    sprintf(buffer, "openvpn{\"config\":{\"address\":\"%s\", \"netmask\":\"%s\", \"mtu\":%d}}\n", 
        ifconfig_local, ifconfig_netmask, tun_mtu);
    sendMessage(buffer);
}

void setupRoute(char* dnsAddress) {
    msg(M_DEBUG, "opensocket.c :: setupRoute");
    strcpy(dnsAddresses, dnsAddress);
}

int setVpnAddress(char* ip)
{
    msg(M_DEBUG, "opensocket.c  :: SetVpnAddress");
    char buffer[BUFFER_SIZE];
	strcpy(buffer, "SetVpnAddress");
	strcat(buffer,ip);

	int result = write(sockfd_s, buffer, strlen(buffer));
	if (result < 0) {
		return -1;
	}

	result = read(sockfd_s, buffer, BUFFER_SIZE - 1);
    return result;
}

int addroute(char* route)
{
    msg(M_DEBUG, "opensocket.c :: addroute");
    char buffer[BUFFER_SIZE];
	strcpy(buffer, "addroute");
	strcat(buffer, route);

	int result = write(sockfd_s, buffer, strlen(buffer));
	if (result < 0) {
		return -1;
	}

	result = read(sockfd_s, buffer, BUFFER_SIZE - 1);
    return result;
}

void updateOpenVpnState(int state)
{
    msg(M_DEBUG, "opensocket.c :: openvpn update state = %d", state);
    if (openvpn_state == state)
    {
        msg(M_DEBUG, "opensocket.c :: openvpn update state is same");
        return;
    }
    
    openvpn_state = state;
    char buffer[BUFFER_SIZE];
    sprintf(buffer, "openvpn{\"updateState\":{\"state\":%d}}\n", openvpn_state);
    sendMessage(buffer);
}

void report_config_err(int err_code)
{
    msg(M_DEBUG, "opensocket.c :: config err code = %d", err_code);
    if (sockfd_s < 0)
    {
        setupClientSocket();
    }
    cfg_err_code = err_code;
    char buffer[BUFFER_SIZE];
    sprintf(buffer, "openvpn{\"updateState\":{\"state\":%d}}\n", cfg_err_code);
    sendMessage(buffer);
}

//private
void *readMsg(void *arg)
{
    char cacheBuffer[BUFFER_SIZE];
    while (true)
    {
        char buffer[BUFFER_SIZE];
        int result = read(sockfd_s, buffer, sizeof(buffer) - 1);
        if (result < 0) 
        {
            msg(M_ERR, "opensocket.c :: ERROR read from socket");
            break;
        } 
        else if (result > 0)
        {
            msg(M_DEBUG, "opensocket.c :: readmsg = %s", buffer);
            if (strcmp(buffer, stop_signal) == 0)
            {
                msg(M_DEBUG, "opensocket.c stop openvpn");
                close(sockfd_s);
                kill(getpid(), SIGTERM);
                break;
            }   

            // modify start 0403 
            // int index = strstr(buffer,"\0");
            // msg(M_DEBUG, "opensocket.c readMsg success, msg = %s, index = %d", buffer, index);
            // if (index >= 0)
            // {
            //     char st[BUFFER_SIZE];
            //     strncmp(st, buffer, index+1);
            //     strcat(cacheBuffer, st);
            //     if (strcmp(cacheBuffer, stop_signal) == 0)
            //     {
            //         msg(M_DEBUG, "opensocket.c stop openvpn %s %d", __FUNCTION__, __LINE__);
            //         close(sockfd_s);
            //         throw_signal(SIGTERM);
            //         break;
            //     }
            //     sleep(50);
            // }
            // else
            // {
            //     strcat(cacheBuffer, buffer);
            // }
            // modify end 0403 
        }
        sleep(100);
    }
}

void sendMessage(char* buffer)
{
    msg(M_DEBUG, "opensocket.c :: start sendMessage");
	int result = write(sockfd_s, buffer, strlen(buffer));
	if (result < 0) {
        msg(M_DEBUG, "opensocket.c  :: ERROR writing to socket");
	}
}

